package cn.qingyun.gis.modules.scenes.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.qingyun.gis.exception.CommonException;
import cn.qingyun.gis.modules.express.entity.ExpressRegion;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.qingyun.gis.exception.CommonException;
import cn.qingyun.gis.modules.express.entity.Point;
import cn.qingyun.gis.modules.express.entity.dto.ExpressRegionNodeDto;
import cn.qingyun.gis.modules.express.response.RegionResponse;
import cn.qingyun.gis.modules.express.util.GeoTool;
import cn.qingyun.gis.modules.init.system.constant.enums.RegionNumPermissionEnum;
import cn.qingyun.gis.modules.init.system.sso.crm.CrmAuthItem;
import cn.qingyun.gis.modules.init.system.sso.utils.CrmTool;
import cn.qingyun.gis.modules.scenes.entity.BoundaryResponse;
import cn.qingyun.gis.modules.scenes.entity.UniAddrRegion;
import cn.qingyun.gis.modules.scenes.mapper.UniAddrRegionMapper;
import cn.qingyun.gis.modules.scenes.request.SubBoundaryRequest;
import cn.qingyun.gis.modules.scenes.service.UniAddrRegionService;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.xml.soap.Node;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author guest
 * @since 2024-05-18
 */
@Slf4j
@Service
@DS("scenes_offensive")
public class UniAddrRegionServiceImpl extends ServiceImpl<UniAddrRegionMapper, UniAddrRegion> implements UniAddrRegionService {

    @Override
    public Integer selectLevel(String code) {
        log.debug("传入的区划码{}",code);
        LambdaQueryWrapper<UniAddrRegion> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(UniAddrRegion::getLevel,UniAddrRegion::getName).eq(UniAddrRegion::getCode,code).ne(UniAddrRegion::getState,-1).last("limit 1");
        UniAddrRegion uniAddrRegion = this.getOne(wrapper);
        if (uniAddrRegion == null) {
            log.debug("传递的区域编码,{}不正确", code);
            throw new CommonException("传递的区域编码不正确！");
        }
        Integer level = uniAddrRegion.getLevel();
        if (level==null){
            throw new CommonException("查询的区域编码级别为空！");
        }
        return level;
    }

    @Override
    public UniAddrRegion selectRegionByCode(String code) {
        log.debug("传入的区划码{}", code);
        LambdaQueryWrapper<UniAddrRegion> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(UniAddrRegion::getLevel, UniAddrRegion::getName,UniAddrRegion::getCode,UniAddrRegion::getParentCode).eq(UniAddrRegion::getCode, code).ne(UniAddrRegion::getState, -1).eq(UniAddrRegion::getCode,code).ne(UniAddrRegion::getState,-1).last("limit 1");
        return this.getOne(wrapper);
    }

    @Override
    public List<UniAddrRegion> selectRegionListByCode(String code) {
        LambdaQueryWrapper<UniAddrRegion> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UniAddrRegion::getParentCode,code).ne(UniAddrRegion::getState,-1);
        return this.baseMapper.selectList(wrapper);
    }

    @Override
    public List<UniAddrRegion> selectRegionList(Integer level, String code) {
        QueryWrapper<UniAddrRegion> wrapper = new QueryWrapper<>();
        wrapper.select("level", "name", "code", "ST_X(center_point) AS lon", "ST_Y(center_point) AS lat")
                .eq("level", level)
                .likeRight("code", code)
                .ne("state", -1);
        return this.baseMapper.selectList(wrapper);
    }

    @Override
    public List<UniAddrRegion> selectRegionListSub(Integer level, String code, BigDecimal lbLon, BigDecimal lbLat, BigDecimal rtLon, BigDecimal rtLat) {
        QueryWrapper<UniAddrRegion> wrapper = new QueryWrapper<>();
        wrapper.select("level", "name", "code", "ST_X(center_point) AS lon", "ST_Y(center_point) AS lat","ST_AsText(wkt) AS wkt")
                .eq("level", level)
                .likeRight("code", code)
                .ne("state", -1)
                .apply(String.format("ST_Intersects(wkt,ST_GeomFromText('MULTIPOLYGON(((%s %s,%s %s,%s %s,%s %s,%s %s)))',4326))",
                        lbLon, lbLat, lbLon, rtLat, rtLon, rtLat, rtLon, lbLat, lbLon, lbLat));
        return this.baseMapper.selectList(wrapper);
    }

    @Override
    public List<UniAddrRegion> selectRegionByParentCodeList(Integer level, String code) {
        QueryWrapper<UniAddrRegion> wrapper = new QueryWrapper<>();
        wrapper.select("level", "name", "code", "ST_X(center_point) AS lon", "ST_Y(center_point) AS lat")
                .eq("level", level)
                .likeRight("parent_code", code)
                .ne("state", -1);
        return this.baseMapper.selectList(wrapper);
    }

    @Override
    public List<UniAddrRegion> selectRegionByParentCodeListSub(Integer level, String code, BigDecimal lbLon, BigDecimal lbLat, BigDecimal rtLon, BigDecimal rtLat) {
        QueryWrapper<UniAddrRegion> wrapper = new QueryWrapper<>();
        wrapper.select("level", "name", "code", "ST_X(center_point) AS lon", "ST_Y(center_point) AS lat","ST_AsText(wkt) AS wkt")
                .eq("level", level)
                .likeRight("parent_code", code)
                .ne("state", -1)
                .apply(String.format("ST_Intersects(wkt,ST_GeomFromText('MULTIPOLYGON(((%s %s,%s %s,%s %s,%s %s,%s %s)))',4326))",
                        lbLon, lbLat, lbLon, rtLat, rtLon, rtLat, rtLon, lbLat, lbLon, lbLat));
        return this.baseMapper.selectList(wrapper);
    }

    @Override
    public List<UniAddrRegion> selectProvinceCityDisList() {
        QueryWrapper<UniAddrRegion> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .select(UniAddrRegion::getCode, UniAddrRegion::getName, UniAddrRegion::getParentCode)
                .in(UniAddrRegion::getLevel, Arrays.asList(1, 2, 3))
                .ne(UniAddrRegion::getState, -1)
                .orderByAsc(UniAddrRegion::getLevel);
        return this.baseMapper.selectList(queryWrapper);
    }

    @Override
    public List<UniAddrRegion> selectRegionByParentCode(String parentCode) {
        QueryWrapper<UniAddrRegion> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().select(UniAddrRegion::getCode, UniAddrRegion::getName, UniAddrRegion::getParentCode).eq(UniAddrRegion::getParentCode, parentCode).or().eq(UniAddrRegion::getCode, parentCode);
        return this.baseMapper.selectList(queryWrapper);
    }

    @Override
    public List<ExpressRegionNodeDto> sonRegionListNew(String code) {
        QueryWrapper<UniAddrRegion> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().select(UniAddrRegion::getCode, UniAddrRegion::getName, UniAddrRegion::getLevel).eq(UniAddrRegion::getParentCode, code);
        List<UniAddrRegion> uniAddrRegionList = this.baseMapper.selectList(queryWrapper);
        List<ExpressRegionNodeDto> expressRegionNodes = Lists.newArrayList();
        for (UniAddrRegion data : uniAddrRegionList) {
            ExpressRegionNodeDto node = new ExpressRegionNodeDto();
            BeanUtil.copyProperties(data, node);
            node.setGridType(RegionNumPermissionEnum.UN_PROVINCE.name()); // 调用此接口返回节点区域等级为非省级
            node.setState("1");
            if (data.getLevel() != null && data.getLevel() == 4) {
                String name = node.getName();
                String[] parts = name.split("-");
                String result = parts.length > 1 ? parts[1] : name;
                node.setName(result);
            }
            if (!node.getName().contains("政企") && !node.getName().contains("校园") && !node.getName().contains("虚拟网格") && !node.getName().contains("商企网格")) {
                expressRegionNodes.add(node);
            }
        }
        expressRegionNodes.sort(Comparator.comparing(ExpressRegionNodeDto::getCode));
        return expressRegionNodes;
    }

    @Override
    public  List<Tree<String>> getRegionList() {
        long startTime = System.currentTimeMillis();
        List<CrmAuthItem> crmGridAuthList = CrmTool.getCrmGridAuth();
        List<RegionResponse> allList = new ArrayList<>();
        for (CrmAuthItem datum : crmGridAuthList) {
            List<RegionResponse> list = new ArrayList<>();
            String[] areaCode = datum.getAllParentCode().split(",");
            String[] areaName = datum.getFullName().split("-");
            if (!Objects.equals(areaCode.length, areaName.length)) {
                throw new CommonException("解析接口-区域编码与名称映射出错！");
            }
            List<String> allCode = new ArrayList<>();
            //如果是省级权限
            if (areaCode.length == 1) {
                QueryWrapper<UniAddrRegion> queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda().select(UniAddrRegion::getCode, UniAddrRegion::getName, UniAddrRegion::getParentCode, UniAddrRegion::getParentName).eq(UniAddrRegion::getLevel, 1).or().eq(UniAddrRegion::getLevel, 2).or().eq(UniAddrRegion::getLevel, 3).orderByAsc(UniAddrRegion::getLevel);
                List<UniAddrRegion> gridList = this.list(queryWrapper);
                list = BeanUtil.copyToList(gridList, RegionResponse.class);
                allCode.add(datum.getAllParentCode());
            } else if (areaCode.length == 2) {//市级权限
                QueryWrapper<UniAddrRegion> queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda().select(UniAddrRegion::getCode, UniAddrRegion::getName, UniAddrRegion::getParentCode, UniAddrRegion::getParentName).eq(UniAddrRegion::getParentCode, datum.getAreaCode()).or().eq(UniAddrRegion::getCode, datum.getAreaCode());
                List<UniAddrRegion> gridList = this.list(queryWrapper);
                QueryWrapper<UniAddrRegion> sdWrapper = new QueryWrapper<>();
                sdWrapper.lambda().select(UniAddrRegion::getCode, UniAddrRegion::getName, UniAddrRegion::getParentCode, UniAddrRegion::getParentName).eq(UniAddrRegion::getLevel, 1);
                UniAddrRegion sd = this.getOne(sdWrapper);
                RegionResponse sdRegion = new RegionResponse();
                BeanUtils.copyProperties(sd, sdRegion);
                sdRegion.setClick(false);
                list = BeanUtil.copyToList(gridList, RegionResponse.class);
                list.add(sdRegion);
                allCode.add(datum.getAllParentCode());
            } else if (areaCode.length == 3) {
                for (int i = 0; i < areaCode.length; i++) {
                    RegionResponse region = new RegionResponse();
                    if (i == 0) {
                        region.setCode(areaCode[i]).setParentCode("0").setName(areaName[i]).setParentName("").setClick(false);
                    } else if (i == 1) {
                        region.setCode(areaCode[i]).setParentCode(areaCode[i - 1]).setName(areaName[i]).setParentName(areaName[i - 1]).setClick(false);
                    } else {
                        region.setCode(areaCode[i]).setParentCode(areaCode[i - 1]).setName(areaName[i]).setParentName(areaName[i - 1]).setClick(true);
                    }
                    list.add(region);
                }
                allCode.add(datum.getAllParentCode());
            } else if (areaCode.length == 4) {
                if (allCode.stream().anyMatch(item -> datum.getAllParentCode().startsWith(item))) {
                    continue;
                }
                for (int i = 0; i < areaCode.length; i++) {
                    RegionResponse region = new RegionResponse();
                    if (i == 0) {
                        region.setCode(areaCode[i]).setParentCode("0").setName(areaName[i]).setParentName("").setClick(false);
                    } else if (i == 1) {
                        region.setCode(areaCode[i]).setParentCode(areaCode[i - 1]).setName(areaName[i]).setParentName(areaName[i - 1]).setClick(false);
                    } else {
                        region.setCode(areaCode[i]).setParentCode(areaCode[i - 1]).setName(areaName[i]).setParentName(areaName[i - 1]).setClick(true);
                    }
                    list.add(region);
                }
            }
            allList.addAll(list);
        }
        //TODO 有隐患，如何根据字段值进行去重
        List<RegionResponse> groupCollect = allList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(RegionResponse::getCode))), ArrayList::new));
        List<RegionResponse> collect = groupCollect.stream().filter(item -> !item.getName().contains("政企") && !item.getName().contains("校园") && !item.getName().contains("虚拟网格") && !item.getName().contains("商企网格")).collect(Collectors.toList());
        TreeNodeConfig config = new TreeNodeConfig();
        config.setIdKey("code");                              //默认id，可以不设置
        config.setParentIdKey("parentCode");                       //父id
        config.setNameKey("name");              //分类名称
        config.setChildrenKey("childNode");              //孩子节点
        config.setWeightKey("code");                     //排序字段
        //3.转树
        List<Tree<String>> list = TreeUtil.build(collect, "SD", config, ((object, treeNode) -> {
            treeNode.putExtra("code", object.getCode());
            treeNode.putExtra("parentCode", object.getParentCode());
            treeNode.putExtra("name", object.getName());
            treeNode.putExtra("parentName", object.getParentName());
            treeNode.putExtra("isClick", object.isClick());
        }));
        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;
        log.info("解析数据执行时间：{}毫秒", executionTime);
        return list;
    }


    @Override
    public List<UniAddrRegion> selectRegionBoundaryList(SubBoundaryRequest request) {
        log.debug("查询区划下边的边界入参{}",request);
        BigDecimal rtLon = request.getRtLon();
        BigDecimal rtLat = request.getRtLat();
        BigDecimal lbLon = request.getLbLon();
        BigDecimal lbLat = request.getLbLat();
        //1.根据编码查区划边界
        QueryWrapper<UniAddrRegion> wrapper = new QueryWrapper<>();
        wrapper.select("id","name","level","ST_AsText(wkt) AS wkt","ST_AsText(center_point) AS centerPoint").eq("code",request.getCode()).ne("state",-1);
        UniAddrRegion uniAddrRegion = this.baseMapper.selectOne(wrapper);
        if (uniAddrRegion==null || StringUtils.isEmpty(uniAddrRegion.getWkt())){
            throw new CommonException("未查询到该码的面数据！");
        }
        QueryWrapper<UniAddrRegion> wrapperSub = new QueryWrapper<>();
        wrapperSub.select("id","name","code","level","ST_AsText(wkt) AS wkt","ST_AsText(center_point) AS centerPoint").eq("level",request.getLevel()).ne("state",-1).apply(String.format("ST_Intersects(wkt,ST_GeomFromText('MULTIPOLYGON(((%s %s,%s %s,%s %s,%s %s,%s %s)))',4326))",
                lbLon, lbLat, lbLon, rtLat, rtLon, rtLat, rtLon, lbLat, lbLon, lbLat)).apply(String.format("ST_Within(center_point,ST_GeomFromText('%s',4326))", uniAddrRegion.getWkt()));
        List<UniAddrRegion> uniAddrRegionList = this.baseMapper.selectList(wrapperSub);
        return uniAddrRegionList.stream().filter(item -> !item.getName().contains("政企") && !item.getName().contains("校园") && !item.getName().contains("虚拟网格") && !item.getName().contains("商企网格")).collect(Collectors.toList());
    }

    @Override
    public List<UniAddrRegion> selectProvinceByCode(int i) {
        return this.baseMapper.selectProvinceByCode(i);
    }

    @Override
    public List<UniAddrRegion> selectUniAddrByCode(String code, Integer flag) {
        return this.baseMapper.selectUniAddrByCode(code,flag);
    }

    @Override
    public UniAddrRegion selectRegionBoundary(String code) {
        log.debug("查询区划边界入参{}",code);
        QueryWrapper<UniAddrRegion> wrapper = new QueryWrapper<>();
        wrapper.select("id","name","code","ST_AsText(wkt) AS wkt","ST_AsText(center_point) AS centerPoint","ST_X(ST_Centroid(wkt)) as lon","ST_Y(ST_Centroid(wkt)) as lat").eq("code",code);
        return this.baseMapper.selectOne(wrapper);
    }


    @Override
    public List<UniAddrRegion> getAncestorsUpToLevel(String microGridCode, int startLevel, int endLevel) {
        List<UniAddrRegion> ancestors = new ArrayList<>();
        String currentCode = microGridCode;
        int currentLevel = startLevel;

        while (currentLevel > endLevel) {
            UniAddrRegion region = this.baseMapper.getRegionByCodeAndLevel(currentCode, currentLevel);
            if (region == null) {
                break;
            }
            ancestors.add(region);
            currentCode = region.getParentCode();
            currentLevel--;
        }

        return ancestors;
    }

}
